﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using CommonClasses.Model;
using Eco.ObjectImplementation;
using Eco.ObjectRepresentation;
using CommonClasses.Utility;
using System.IO;
using EcoExtensions.Model;
using MockDocElems.Model;
using System.Collections;
using CommonClasses.Logger;
using MockCompUnit.Model;
using CommonClasses;

namespace MockControl
{

    /// <summary>
    /// Плагин, тестирующий IDE
    /// </summary>
    public partial class MockControl : UserControl, IDocumentEditor, IDocumentsFactory
    {

        #region private Fields

        /// <summary>
        /// Управление логами
        /// </summary>
        private ILoggingManagement logManagment = null;

        /// <summary>
        /// Логгер
        /// </summary>
        private ILogger logger = null;

        /// <summary>
        /// Хранит среду ECO
        /// </summary>
        private IEcoServiceProvider serviceProvider;

        /// <summary>
        /// Cписок контролов, сам данный контрол будет фабрикой (просто так получится)
        /// </summary>
        private List<MockControl> AllControls = new List<MockControl>();

        /// <summary>
        /// Имя
        /// </summary>
        private string nameF;

        /// <summary>
        /// Структура документа
        /// </summary>
        private List<EcoObject> docStruct = null;

        /// <summary>
        /// Соответствие Text к элементу документа
        /// </summary>
        private Dictionary<TextBox, EcoObject> textBoxToElem = new Dictionary<TextBox, EcoObject>();

        /// <summary>
        /// Последний выделенный элемент документа (IDE вызывает OnSelect для него)
        /// </summary>
        private IDocStructElement LastSelectedElem = null;

        /// <summary>
        /// Список иконок элементов документов
        /// </summary>
        private List<Bitmap> docElemIcons = new List<Bitmap>();

        /// <summary>
        /// Генератор случайных чисел
        /// </summary>
        private Random random = new Random();
        #endregion


        #region public Events
        /// <summary>
        /// Событие: изменение Dirty-состояния данного документа
        /// </summary>
        public event EventHandler OnDirtyChanged;


        /// <summary>
        /// Событие: изменение структуры данного документа
        /// </summary>
        public event EventHandler OnStrucChanged;
        #endregion


        #region Invokers of events

        public void DirtyChanged()
        {
            if (OnDirtyChanged != null)
                OnDirtyChanged(this, null);
        }

        public void StructChanged()
        {
            if (OnStrucChanged != null)
                OnStrucChanged(this, null);
        }
        #endregion


        public MockControl(IEcoServiceProvider serviceProvider, ILoggingManagement logManagment)
        {
            this.serviceProvider = serviceProvider;
            this.logManagment = logManagment;
            this.logger = logManagment.GetLogger("MockControlLogger");
            InitializeComponent();
            this.Name = "MockDocumentManager";
            docStruct = new List<EcoObject>();
            docElemIcons.Add(global::MockControl.Properties.Resources.PAGENUM);
            docElemIcons.Add(global::MockControl.Properties.Resources.PAGENUM2);
            docElemIcons.Add(global::MockControl.Properties.Resources.PAGENUM3);
            docElemIcons.ForEach(bmp=> bmp.MakeTransparent(bmp.GetPixel(0,0)) );

            AddNewElem();
            AddNewElem();
        }


        /// <summary>
        /// Добавить новый элемент документа при заданном имени
        /// </summary>
        /// <param name="elemName">имя элемента</param>
        /// <param name="elemText">текст для этого элемента</param>
        private void AddNewNamedElem(string elemName, string elemText)
        {
            TextBox chb = new TextBox();
            chb.Name = elemName;
            chb.Text = elemText;
            flowLayoutPanel1.Controls.Add(chb);

            MockDocStrucElem1 docElement = new MockDocStrucElem1(serviceProvider);
            docElement.MockStructElementSelectedEvent += new MockDocElems.Model.MockDocStrucElem1.MockStructElementSelectedHandler(docElement_MockStructElementSelectedEvent);
            docElement.MockElemDataChangedEvent += new MockDocStrucElem1.MockElemDataChangedHandler(docElement_MockElemDataChangedEvent);
            docElement.ParentDocStructElement = null;
            docElement.ParentDocEditor = this;//new EcoObject(serviceProvider, this);
            docElement.SubDocElements.Clear();
            docElement.Name = elemName;
            docElement.Icon = docElemIcons[random.Next(docElemIcons.Count)];

            docElement.PropertyTable = new EcoObject(serviceProvider, new PropertyTable());
            PropertySpec ps;
            ps = new PropertySpec("Название", typeof(string), "Описание мокограммы :)", "");
            (docElement.PropertyTable.Object as PropertyTable).Properties.Add(ps);
            (docElement.PropertyTable.Object as PropertyTable).GetValue += new PropertySpecEventHandler(MockControl_GetValue);
            (docElement.PropertyTable.Object as PropertyTable).SetValue += new PropertySpecEventHandler(MockControl_SetValue);

            EcoObject docElem = new EcoObject(serviceProvider, docElement);
            docStruct.Add(docElem);

            textBoxToElem.Add(chb, docElem);
            StructChanged();
        }


        /// <summary>
        /// Добавить новый элемент документа
        /// </summary>
        private void AddNewElem()
        {
            string chbName;
            int i = 1;
            bool work = true;
            do
            {
                chbName = "textBox" + i.ToString();
                i++;
                work = false;
                foreach (var key in textBoxToElem.Keys)
                    if (key.Name == chbName) work = true;

            }
            while (work);
            TextBox chb = new TextBox();
            chb.Name = chbName;
            chb.Text = chbName;
            flowLayoutPanel1.Controls.Add(chb);

            MockDocStrucElem1 docElement = new MockDocStrucElem1(serviceProvider);
            docElement.MockStructElementSelectedEvent += new MockDocElems.Model.MockDocStrucElem1.MockStructElementSelectedHandler(docElement_MockStructElementSelectedEvent);
            docElement.MockElemDataChangedEvent += new MockDocStrucElem1.MockElemDataChangedHandler(docElement_MockElemDataChangedEvent);
            docElement.ParentDocStructElement = null;
            docElement.ParentDocEditor = this;//new EcoObject(serviceProvider, this);
            docElement.SubDocElements.Clear();
            docElement.Name = chbName;
            docElement.Icon = docElemIcons[random.Next(docElemIcons.Count)];

            docElement.PropertyTable = new EcoObject(serviceProvider, new PropertyTable());
            PropertySpec ps;
            ps = new PropertySpec("Название", typeof(string), "Описание мокограммы :)", "");
            (docElement.PropertyTable.Object as PropertyTable).Properties.Add(ps);
            (docElement.PropertyTable.Object as PropertyTable).GetValue += new PropertySpecEventHandler(MockControl_GetValue);
            (docElement.PropertyTable.Object as PropertyTable).SetValue += new PropertySpecEventHandler(MockControl_SetValue);

            EcoObject docElem = new EcoObject(serviceProvider, docElement);
            docStruct.Add(docElem);

            textBoxToElem.Add(chb, docElem);
            StructChanged();
            button2.Enabled = true;
        }


        /// <summary>
        /// Удалить элемент документа
        /// <param name="elem">элементк документа</param>
        /// </summary>
        private void RemoveItem(MockDocStrucElem1 elem)
        {
            var docElems = from docitem in textBoxToElem.Values
                           where (docitem.Object as MockDocStrucElem1) == elem
                           select docitem;

            var docElem = docElems.ElementAt(0);
            var txtBoxs = from tbox in textBoxToElem.Keys
                          where (textBoxToElem[tbox] == docElem)
                          select tbox;
            var txtBox = txtBoxs.ElementAt(0);


            flowLayoutPanel1.Controls.Remove(txtBox);
            docStruct.Remove(docElem);
            textBoxToElem.Remove(txtBox);
            StructChanged();
            if (textBoxToElem.Count == 0)
                button2.Enabled = false; ;
        }


        /// <summary>
        /// Удалить последний в списке элемент документа
        /// </summary>
        private void RemoveLastItem()
        {
            var chb = textBoxToElem.Keys.Last();
            var docElem = textBoxToElem[chb];
            flowLayoutPanel1.Controls.Remove(chb);
            docStruct.Remove(docElem);
            textBoxToElem.Remove(chb);
            StructChanged();
            if (textBoxToElem.Count == 0)
                button2.Enabled = false; ;

        }


        /// <summary>
        /// Загрузить данные из файла
        /// </summary>
        /// <param name="FileName">путь до файла</param>
        public void LoadData(string FileName)
        {
            while (flowLayoutPanel1.Controls.Count != 0)
                RemoveLastItem();

            List<string> dataTemp = null;
            string dataTextTemp = null;

            PathUtility.RestoreCurrentDirectory(Locality.CurrentProjectDir);

            try
            {
                dataTemp = SerializationUtility.DeserializeFromFile<List<string>>(FileName);
                dataTextTemp = SerializationUtility.DeserializeFromFile<string>(FileName + ".2");
            }
            catch (Exception exception)
            {
                throw new LoadingDocumentException("Ошибка загрузки файла данных " + System.IO.Path.GetFullPath(FileName));
            }

            rtbCode.Text = dataTextTemp;
            IEnumerator<string> data = dataTemp.GetEnumerator();
            //try
            //{
            data.Reset();
            data.MoveNext();
            int Count = int.Parse(data.Current);
            data.MoveNext();
            chbLoadExcept.Checked = bool.Parse(data.Current);
            if (chbLoadExcept.Checked)
                throw new LoadingDocumentException("Смоделированное исключение");
            data.MoveNext();
            chbLoadDMExcept.Checked = bool.Parse(data.Current);
            if (chbLoadDMExcept.Checked)
                throw new LoadingDomainModelCorrespException("Смоделированное исключение");

            for (int i = 0; i < Count; i++)
            {
                data.MoveNext();
                string elemName = data.Current;
                data.MoveNext();
                string elemText = data.Current;
                AddNewNamedElem(elemName, elemText);
            }

            //}
            //catch (Exception e)
            // {
            //    e.Message = "Ошибка загрузки файла данных " + System.IO.Path.GetFullPath(FileName) + ". " + e.Message;
            //    throw e;
            // }

            this.Path = PathUtility.MakePathLocal( PathUtility.GetPathWithoutExtension(FileName), Locality.CurrentProjectDir);
        }


        #region DocElements' event handlers
        /// <summary>
        /// Обработчик изменения структуры элемента документа
        /// </summary>
        private void docElement_MockElemDataChangedEvent()
        {
            this.StructChanged();
        }


        /// <summary>
        /// Обработчик выделения элемента документа
        /// </summary>
        /// <param name="elem">элемент документа</param>
        private void docElement_MockStructElementSelectedEvent(MockDocStrucElem1 elem)
        {
            this.LastSelectedElem = elem;
        }


        /// <summary>
        /// Обработчик нажатия на элемент меню элемента докумнта
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void docElemMenuItem_Click(object sender, EventArgs e)
        {
            var menuItem = sender as ToolStripMenuItem;
            switch (menuItem.Text)
            {
                case "Добавить новую мокограмму":
                    AddNewElem();
                    break;
                case "Удалить":
                    RemoveItem((menuItem.Owner as ContextMenuStrip).Tag as MockDocStrucElem1);
                    break;
            }
        }
        #endregion


        #region Properties handlers

        /// <summary>
        /// Запрос на установление значения свойства элемента документа
        /// </summary>
        /// <param name="sender">sender</param>
        /// <param name="e">PropertySpecEventArgs</param>
        private void MockControl_SetValue(object sender, PropertySpecEventArgs e)
        {
            if (LastSelectedElem != null)
                switch (e.Property.Name)
                {
                    case "Название":
                        string newName = (string)e.Value;

                        if (newName == "")
                        {
                            ErrorsUtility.Error("Название элемента документа '"+ newName + "' некорректно. Выберите другое название. ", logger);
                            return;
                        }

                        foreach (var key in textBoxToElem.Keys)
                        {
                            if (key.Name == newName)
                            {
                                ErrorsUtility.Error("Элемент документа '" + System.IO.Path.GetFileNameWithoutExtension((textBoxToElem[key].Object as MockDocStrucElem1).ParentDocEditor.Path) + "' с названием '" + newName + "' уже существует в данном документе. Выберите другое название. ", logger);
                                return;
                            }
                        }
                        var tbList = from k in textBoxToElem.Keys
                                     where k.Name == LastSelectedElem.Name
                                     select k;
                        if (tbList.Count() != 0)
                        {
                            (tbList.ElementAt(0) as TextBox).Text = newName;
                            (tbList.ElementAt(0) as TextBox).Name = newName;
                        }
                        LastSelectedElem.Name = newName;

                        break;
                }
        }


        /// <summary>
        /// Запрос на чтение значения свойства элемента документа
        /// </summary>
        /// <param name="sender">sender</param>
        /// <param name="e">PropertySpecEventArgs</param>
        private void MockControl_GetValue(object sender, PropertySpecEventArgs e)
        {
            if (LastSelectedElem != null)
                switch (e.Property.Name)
                {
                    case "Название":
                        e.Value = LastSelectedElem.Name;
                        break;
                }
        }
        #endregion


        #region IDocumentEditor Members

        public ContextMenuStrip GetDocElemMenu(IDocStructElement DocStructElement)
        {
            var menu = new ContextMenuStrip();
            menu.Tag = DocStructElement;
            menu.Items.Add(new ToolStripMenuItem("Добавить новую мокограмму"));
            menu.Items[menu.Items.Count - 1].Click += new EventHandler(docElemMenuItem_Click);
            menu.Items.Add(new ToolStripMenuItem("Удалить"));
            menu.Items[menu.Items.Count - 1].Click += new EventHandler(docElemMenuItem_Click);
            return menu;
        }

        public IEcoList<EcoObject> GetDocStruct()
        {
            return (new ObjectListAdapter<EcoObject>(docStruct));
        }

        public void Initialize(Control control)
        {
            this.Parent = control;
        }

        public void OnDeselect()
        {
            return;
        }

        public void OnSelect()
        {
            return;
        }

        public void Rename(string NewName)
        {
            throw new NotImplementedException();
            //this.Name = NewName;
        }

        public void SaveAs(string path)
        {
            // сохраним значение textBox1.string и TextBox2.string

            var data = new List<string>();
            data.Add(flowLayoutPanel1.Controls.Count.ToString());
            data.Add(chbLoadExcept.Checked.ToString());
            data.Add(chbLoadDMExcept.Checked.ToString());
            foreach (TextBox tb in flowLayoutPanel1.Controls)
            {
                data.Add(tb.Name);
                data.Add(tb.Text);
            }

            PathUtility.RestoreCurrentDirectory(Locality.CurrentProjectDir);
            SerializationUtility.SerializeToFile<List<string>>(path, data);
            SerializationUtility.SerializeToFile<string>(path + ".2", rtbCode.Text);

            this.Path = PathUtility.GetPathWithoutExtension(path);
        }

        public void ShowError(Error error)
        {
            MessageBox.Show(
                "Error at string '" + rtbCode.Lines[error.Point.Point.Y - 1] + "'",
                "И тут на документе '" + System.IO.Path.GetFileNameWithoutExtension(this.Path) + "' подсвечивается ошибка");
        }

        public void ShowExecutePlace(ILPMEDTextPoint breakpoint)
        {
            throw new NotImplementedException();
        }

        public void Verify()
        {
            throw new NotImplementedException();
        }

        public void WatchStateChanged(ILPMEDVariable variable)
        {
            throw new NotImplementedException();
        }

        public string Comment
        {
            get;
            set;
        }

        private bool isCompiledF = true;

        public bool IsCompiled
        {
            get
            {
                return isCompiledF;
            }
            set
            {
                isCompiledF = value;
            }
        }

        public bool IsDirty
        {
            get;
            set;
        }

        public bool IsReadOnly
        {
            get;
            set;
        }

        private string pathF = "";

        public string Path
        {
            get{return pathF;}
            set
            {
                string path_Old = this.Path;
                pathF = value;
                if (path_Old != value)
                    StructChanged();
            }
        }

        #endregion


        /// <summary>
        /// Выгрузить Editor и все, что с ним связано (что он насоздавал)
        /// </summary>
        /// <remarks>
        /// обращаем внимание только на экошные объекты:
        /// MockDocStructelem1 пусть удалит errorList после очищения ошибок
        /// DocStrucElem удалим мы
        /// </remarks>
        /// 
        public void OnMockEditorUnload()
        {
            docStruct.ForEach(docElem => docElem.AsIObject().Delete());
            docStruct = null;
            GC.Collect();
        }


        #region IDocumentsFactory Members

        public void ChangeNodesIsCompiled(Dictionary<Descriptions_DomainModel.Model.Node, bool> NodesIsCompiled)
        {
            throw new NotImplementedException();
        }

        public void DeleteDocument(IDocumentEditor DocumentEditor)
        {
            PathUtility.RestoreCurrentDirectory(Locality.CurrentProjectDir);

            //удалить файл
            if (File.Exists(DocumentEditor.Path + ".mock"))
                File.Delete(DocumentEditor.Path + ".mock");

            //удалить из проекта
            RemoveDocument(DocumentEditor);
        }

        public ErrorReport GetCompilationUnits(out List<ICompilationUnit> units)
        {
            units = new List<ICompilationUnit>();
            List<MockCompilationUnit> mockUnits =  new List<MockCompilationUnit>();
            foreach (var ctrl in AllControls)
            {
                if (ctrl.IsCompiled)
                {
                    MockCompilationUnit u = new MockCompilationUnit(serviceProvider);

                    // для каждого модуля будет автоматически срабатывать обработчик нужного контрола
                    u.BreakpointFiredEvent += new MockCompilationUnit.BreakpointFiredEventHandler(ctrl.CompilationUnit_BreakpointFiredEvent);
                    u.VariableStateChangedEvent += new MockCompilationUnit.VariableStateChangedHandler(ctrl.CompilationUnit_VariableStateChangedEvent);
                    u.editor = ctrl;
                    u.LPMEDCode = ctrl.rtbCode.Text;
                    mockUnits.Add(u);
                }
            }

            foreach(var mu in mockUnits)
              units.Add(mu);

            return null;
        }

        public void Initialize(Descriptions_DomainModel.Model.DomainModel DomainModel)
        {
            throw new NotImplementedException();
        }

        public IDocumentEditor LoadDocument(string FileName)
        {
            var newMock = new MockControl(serviceProvider, logManagment);
            newMock.LoadData(FileName);
            AllControls.Add(newMock);
            return newMock;
        }

        public IDocumentEditor NewDocument(IList<string> modulesPaths)
        {
            var newMock = new MockControl(serviceProvider, logManagment);
            int i = 0;
            do
            {
                i++;
                newMock.Path = ".\\MockDocument" + i.ToString();
            }
            while (modulesPaths.Contains(System.IO.Path.GetFileNameWithoutExtension(newMock.Path)));
            AllControls.Add(newMock);
            return newMock;
        }

        public void RemoveDocument(IDocumentEditor DocumentEditor)
        {
            if (!(DocumentEditor is MockControl))
                throw new Exception("A try to remove a document which is not а MockControl");
            if (!AllControls.Contains(DocumentEditor as MockControl))
                throw new Exception("A try to remove a document which is not present in Mock documents' list");
            (DocumentEditor as MockControl).OnMockEditorUnload();
            AllControls.Remove(DocumentEditor as MockControl);
        }

        public void RefreshCompiledNodes(IList<Descriptions_DomainModel.Model.Node> compiledNodes)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Удалить все редакторы, элементы документов
        /// </summary>
        public void OnProjectUnloaded()
        {
            AllControls.ForEach(ctrl => ctrl.OnMockEditorUnload());
            AllControls.Clear();
            GC.Collect();
        }

        #endregion


        #region CompilationUnits' events handlers
        public void CompilationUnit_VariableStateChangedEvent(MockCompilationUnit sender, ILPMEDVariable variable)
        {
            MessageBox.Show("CompilationUnit_VariableStateChangedEvent!!!");
            throw new NotImplementedException("MockVariables'changes are not imlemented at the version");
        }

        public void CompilationUnit_BreakpointFiredEvent(MockCompilationUnit sender, ILPMEDTextPoint textpoint)
        {
            MessageBox.Show("CompilationUnit_BreakpointFiredEvent!!!");
            rtbCode.SelectionColor = Color.Red;
            rtbCode.SelectedText = rtbCode.Lines[textpoint.LineNumber];
        }

        #endregion


        #region Visual components' events
        /// <summary>
        /// Определяет состояние IsDirty документа
        /// </summary>
        /// <param name="sender">sender</param>
        /// <param name="e">EventArgs</param>
        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            this.IsDirty = chbIsDirty.Checked;
            this.DirtyChanged();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            AddNewElem();
            button2.Enabled = true;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            RemoveLastItem();
            if (docStruct.Count == 0)
                button2.Enabled = false;
        }
        #endregion

    }

}
